﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GDI_应用案例.Pages.Controls
{
    public delegate void TextSelectionCodeHandle(TextSelectionCodeControl control, bool status);
    public unsafe class TextSelectionCodeControl : Control
    {
        private int _radius = 5;
        private int _rectSize = 1;
        private Color _rectColor = Color.FromArgb(100, Color.Gray);
        private Color _fillColorStart = Color.White;
        private Color _fillColorEnd = Color.White;
        private LinearGradientMode _fillMode = LinearGradientMode.Vertical;
        private int _topHeight = 10;
        private Color _topColor = Color.FromArgb(0, 112, 224);
        private bool _showRect = true;
        private Font _tipFont = new Font("宋体", 12);
        private Color _tipColor = Color.Black;
        private string _tipText = "请在下图依次点击";
        private int _buttonHeight = 50;
        private Color _disableButtonFillColor = Color.FromArgb(112, 168, 224);
        private Color _buttonFillColor = Color.FromArgb(0, 112, 224);
        private Color _buttonHoverColor = Color.FromArgb(112, 168, 224);
        private string _buttonText = "确定";
        private Color _disableButtonForeColor = Color.FromArgb(128, Color.White);
        private Color _buttonForeColor = Color.White;
        private Font _buttonFont = new Font("宋体", 14, FontStyle.Bold);
        private readonly TextSelectionCode _textSelectionCode = new TextSelectionCode();
        private int _clickSize = 30;
        private readonly string _key = Guid.NewGuid().ToString();
        private Color _successColor= Color.FromArgb(57, 197, 34);
        private Color _failureColor= Color.FromArgb(255, 93, 57);
        public TextSelectionCodeControl()
        {
            this.DoubleBuffered = true;
            this.BackColor = Color.White;
            this.Padding = new Padding(20, 10, 20, 10);
            this.ForeColor = Color.Black;
            this.Font = new Font("宋体", 20, FontStyle.Bold);
            this.Size = new Size(380, 425);
            this.MinimumSize = this.Size;
            _timer = new Timer();
            _timer.Interval = 1000;
            _timer.Tick += _timer_Tick;
            this.Disposed += TextSelectionCodeControl_Disposed;
        }

        private void TextSelectionCodeControl_Disposed(object sender, EventArgs e)
        {
            _textSelectionCode.Dispose();
        }

        private void _timer_Tick(object sender, EventArgs e)
        {
            double s = (DateTime.Now - _time).TotalSeconds;
            if(_clickStatus)
            {
                if (s>=2)
                {
                    _timer.Stop();
                    Create();
                }
            }
            else
            {
                if (s >= 1d)
                {
                    _timer.Stop();
                    Create();
                }
            }
        }

        public int Radius
        {
            get => _radius;
            set
            {
                int ra = Math.Max(value, 0);
                if (ra == _radius) return;
                _radius = value;
                this.Invalidate();
            }
        }
        public int RectSize
        {
            get => _rectSize;
            set
            {
                var size = Math.Max(value, 0);
                if (size == _rectSize) return;
                _rectSize = value;
                this.Invalidate();
            }
        }
        public Color RectColor
        {
            get => _rectColor;
            set
            {
                _rectColor = value;
                this.Invalidate();
            }
        }
        public bool ShowRect
        {
            get => _showRect;
            set
            {
                if (_showRect == value) return;
                _showRect = value;
                this.Invalidate();
            }
        }
        public Color FillColorStart
        {
            get => _fillColorStart;
            set
            {
                _fillColorStart = value;
                this.Invalidate();
            }
        }
        public Color FillColorEnd
        {
            get => _fillColorEnd;
            set
            {
                _fillColorEnd = value;
                this.Invalidate();
            }
        }
        public LinearGradientMode FillMode
        {
            get => _fillMode;
            set
            {
                if (_fillMode == value) return;
                _fillMode = value;
                this.Invalidate();
            }
        }
        public int TopHeight
        {
            get => _topHeight;
            set
            {
                int temp = Math.Max(value, 0);
                if (temp == _topHeight) return;
                _topHeight = temp;
                this.Invalidate();
            }
        }
        public Color TopColor
        {
            get => _topColor;
            set
            {
                _topColor = value;
                this.Invalidate();
            }
        }
        public Font TipFont
        {
            get => _tipFont;
            set
            {
                _tipFont = value;
                this.Invalidate();
            }
        }
        public Color TipColor
        {
            get => _tipColor;
            set
            {
                if (_tipColor == value) return;
                _tipColor = value;
                this.Invalidate();
            }
        }
        public string TipText
        {
            get => _tipText;
            set
            {
                if (_tipText == value) return;
                _tipText = value;
                this.Invalidate();
            }
        }
        public int ButtonHeight
        {
            get => _buttonHeight;
            set
            {
                if (_buttonHeight == value) return;
                _buttonHeight = value;
                this.Invalidate();
            }
        }
        public Color DisableButtonFillColor
        {
            get => _disableButtonFillColor;
            set
            {
                if (value == _disableButtonFillColor) return;
                _disableButtonFillColor = value;
                this.Invalidate();
            }
        }
        public Color ButtonFillColor
        {
            get => _buttonFillColor;
            set
            {
                if (_buttonFillColor == value) return;
                _buttonFillColor = value;
                this.Invalidate();
            }
        }
        public Color ButtonHoverColor
        {
            get => _buttonHoverColor;
            set
            {
                _buttonHoverColor = value;
                this.Invalidate();
            }
        }
        public string ButtonText
        {
            get => _buttonText;
            set
            {
                if (_buttonText == value) return;
                _buttonText = value;
                this.Invalidate();
            }
        }
        public Color DisableButtonForeColor
        {
            get => _disableButtonForeColor;
            set
            {
                if (value == _disableButtonForeColor) return;
                _disableButtonForeColor = value;
                this.Invalidate();
            }
        }
        public Color ButtonForeColor
        {
            get => _buttonForeColor;
            set
            {
                if (value == _buttonForeColor) return;
                _buttonForeColor = value;
                this.Invalidate();
            }
        }
        public Font ButtonFont
        {
            get => _buttonFont;
            set
            {
                if (_buttonFont == value) return;
                _buttonFont = value;
                this.Invalidate();
            }
        }
        public TextSelectionCode SelectionCode => _textSelectionCode;
        public int ClickSize
        {
            get => _clickSize;
            set
            {
                int temp = Math.Max(0, value);
                if (_clickSize == temp) return;
                _clickSize = temp;
                this.Invalidate();
            }
        }
        public Color SuccessColor
        {
            get=>_successColor;
            set
            {
                if (_successColor == value) return;
                _successColor = value;
                this.Invalidate();
            }
        }
        public Color FailureColor
        {
            get => _failureColor;
            set
            {
                if (_failureColor == value) return;
                _failureColor = value;
                this.Invalidate();
            }
        }
        public event TextSelectionCodeHandle StatusEvent;

        private TextSelectionCodeResult _result = null;
        public void Create()
        {
            _stopwatch.Stop();
            _isClick = false;
            _curIndex = 0;
            _result?.Bitmap?.Dispose();
            var result = _textSelectionCode.Create(_key);
            if (result.Status)
            {
                _result = result;
                this.Invalidate();
            }
            else
            {
                _result = null;
                MessageBox.Show(result.Message, "创建异常", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //_result.Bitmap.Save($@"C:\Users\JS114295\Desktop\新建文件夹\{DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss")}.png");
        }

        private bool _isHover = false;
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (DesignMode) return;
            if (_imageRect != Rectangle.Empty && _imageRect.Contains(e.Location))
                this.Cursor = Cursors.Hand;
            else
                this.Cursor = Cursors.Default;

            if (_buttonRect != Rectangle.Empty && _buttonRect.Contains(e.Location))
            {
                this.Cursor = Cursors.Hand;
                _isHover = true;
                this.Invalidate();
            }
            else
            {
                this.Cursor = Cursors.Default;
                _isHover = false;
                this.Invalidate();
            }
        }
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);

        }
        private readonly Point[] _clickPoint = new Point[6];
        private int _curIndex = 0;
        private readonly Stopwatch _stopwatch = new Stopwatch();
        private bool _clickStatus = false;
        private bool _isClick = false;
        private readonly Timer _timer;
        private DateTime _time= DateTime.MinValue;
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            if (DesignMode) return;
            if (_result == null || string.IsNullOrEmpty(_result.Key)) return;
            if (_imageRect.Contains(e.Location))
            {
                if (!_stopwatch.IsRunning)
                    _stopwatch.Restart();

                var bigRect = new Rectangle(e.Location.X - (ClickSize >> 1), e.Location.Y - (ClickSize >> 1), ClickSize, ClickSize);
                bool has = false;
                for (int i = 0; i < _curIndex; i++)
                {
                    var p = _clickPoint[i];
                    if (bigRect.Contains(p))
                    {
                        for (int j = i; j < _clickPoint.Length - 1; j++)
                        {
                            _clickPoint[j] = _clickPoint[j + 1];
                        }
                        --_curIndex;
                        has = true;
                        break;
                    }
                }

                if (!has && _curIndex < _clickPoint.Length)
                    _clickPoint[_curIndex++] = e.Location;

                this.Invalidate();
            }
            else
            {
                if (_buttonRect != Rectangle.Empty && _curIndex > 0 && _buttonRect.Contains(e.Location))
                {
                    if (_isClick) return;

                    _stopwatch.Stop();
                    Point[] clickPoint = new Point[_curIndex];
                    Array.Copy(_clickPoint, clickPoint, _curIndex);
                    // 获取图像控件的宽度和高度
                    int controlWidth = _imageRect.Width;
                    int controlHeight = _imageRect.Height;
                    // 获取原始图像的宽度和高度
                    int imageWidth = _result.Bitmap.Width;
                    int imageHeight = _result.Bitmap.Height;
                    // 计算缩放比例
                    float scaleX = (float)imageWidth / controlWidth;
                    float scaleY = (float)imageHeight / controlHeight;
                    fixed(Point* ptr= clickPoint)
                    {
                        for (int i = 0; i < clickPoint.Length; i++)
                        {
                            Point* p = ptr + i;
                            p->X = (int)((p->X-_imageRect.Left) * scaleX);
                            p->Y = (int)((p->Y-_imageRect.Top) * scaleY);
                        }
                    }
                    
                    _clickStatus = _textSelectionCode.Comparison(clickPoint, _result);
                    _isClick = true;
                    _timer.Start();
                    _time = DateTime.Now;
                    StatusEvent?.Invoke(this, _clickStatus);
                    this.Invalidate();
                }
            }
        }
        private Rectangle _imageRect = Rectangle.Empty;
        private Rectangle _buttonRect = Rectangle.Empty;
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.Smooth();
            int w = Width - 1;
            int h = Height - 1;
            Rectangle rect = new Rectangle(0, 0, w, h);
            GraphicsPath path = CreatePath(rect, Radius);
            // 绘制背景
            Fill(g, path, rect);
            // 绘制边框
            if (ShowRect)
                DrawRect(g, path);
            // 绘制头部边框
            FillTopRect(g, w);
            // 绘制文字
            float y = DrawText(g, w);
            // 绘制图片
            _imageRect = DrawCodeImage(g, w, ref y);
            // 绘制按钮
            _buttonRect = DrawButton(g, w, ref y);
            // 绘制点击的圆
            DrawClickPoint(g);
            // 绘制结果
            DrawResult(g);
            DrawRefresh(g);
            path.Dispose();
            g.Smooth(false);
        }

        private float startAngle = 45; // 圆弧起始角度
        private float sweepAngle = 320; // 圆弧扫过的角度

        private void DrawRefresh(Graphics g)
        {
            // 定义圆弧的矩形区域
            Rectangle rect = new Rectangle(_buttonRect.Left, _buttonRect.Top + _buttonRect.Height + 15, 22, 22);
            // 绘制圆弧
            using (Pen pen = new Pen(Color.Blue, 3))
            {
                g.DrawArc(pen, rect, startAngle, sweepAngle);
            }

            // 计算圆弧终点坐标和方向
            float endAngle = startAngle + sweepAngle;
            endAngle %= 360; // 确保角度在0-359度之间
            float radius = rect.Width / 2f;
            float centerX = rect.Left + radius;
            float centerY = rect.Top + radius;

            // 转换为弧度
            double radians = endAngle * Math.PI / 180.0;
            float cosTheta = (float)Math.Cos(radians);
            float sinTheta = (float)Math.Sin(radians);

            // 终点坐标
            float endX = centerX + radius * cosTheta;
            float endY = centerY + radius * sinTheta;

            // 切线方向向量（顺时针方向）
            float tangentX = -sinTheta;
            float tangentY = cosTheta;

            // 箭头参数
            float arrowLength = 5; // 箭头长度
            float arrowAngle = 40; // 箭头开合角度

            // 计算箭头两侧方向
            double beta = arrowAngle * Math.PI / 180.0;
            float cosBeta = (float)Math.Cos(beta);
            float sinBeta = (float)Math.Sin(beta);

            // 左侧边方向（顺时针旋转arrowAngle）
            float leftDirX = tangentX * cosBeta + tangentY * sinBeta;
            float leftDirY = -tangentX * sinBeta + tangentY * cosBeta;

            // 右侧边方向（逆时针旋转arrowAngle）
            float rightDirX = tangentX * cosBeta - tangentY * sinBeta;
            float rightDirY = tangentX * sinBeta + tangentY * cosBeta;

            // 计算箭头顶点
            PointF arrowTip = new PointF(endX, endY);
            PointF leftPoint = new PointF(
                endX + leftDirX * arrowLength,
                endY + leftDirY * arrowLength);
            PointF rightPoint = new PointF(
                endX + rightDirX * arrowLength,
                endY + rightDirY * arrowLength);

            // 绘制箭头
            using (Pen arrowPen = new Pen(Color.Blue, 3))
            {
                g.DrawLine(arrowPen, arrowTip, leftPoint);
                g.DrawLine(arrowPen, arrowTip, rightPoint);
            }
        }
        private void DrawResult(Graphics g)
        {
            if (!_isClick) return;
           
            const int H = 40;
            var rect = new Rectangle(_imageRect.Left, _imageRect.Top + _imageRect.Height - H, _imageRect.Width, H);
            var path= CreateBottomRoundedPath(rect, Radius);
            Brush brush = new SolidBrush(_clickStatus ? SuccessColor : FailureColor);
            g.FillPath(brush, path);
            Brush foreBrush = new SolidBrush(Color.White);
            if (_clickStatus)
            {
                float seconds = (float)Math.Round(_stopwatch.Elapsed.TotalSeconds, 1);
                string text = GetText(seconds);
                SizeF sf = g.MeasureString(text, _tipFont);
                g.DrawString(text, _tipFont, foreBrush, rect.Left + (rect.Width - sf.Width) / 2f, rect.Top + (rect.Height - sf.Height) / 2f);
            }
            else
            {
                string text = "验证失败，请重新尝试";
                SizeF sf=g.MeasureString(text, _tipFont);
                g.DrawString(text, _tipFont, foreBrush, rect.Left + (rect.Width - sf.Width) / 2f, rect.Top + (rect.Height - sf.Height) / 2f);
            }
            foreBrush.Dispose();
            brush.Dispose();
            path.Dispose();
        }
        private string GetText(float time)
        {
            int s = (int)Math.Ceiling(time);
            int t = 101 - (s << 1);
            if (t < 1)
                t = 1;

            return $"{time} 秒的速度超过{t}%的用户";
        }
        private void DrawClickPoint(Graphics g)
        {
            if (_curIndex == 0) return;
            Brush brushBig = new SolidBrush(Color.White);
            Brush brushSmall = new SolidBrush(ButtonFillColor);
            for (int i = 0; i < _curIndex; i++)
            {
                var p = _clickPoint[i];
                var bigRect = new Rectangle(p.X - (ClickSize >> 1), p.Y - (ClickSize >> 1), ClickSize, ClickSize);
                int size = ClickSize - 6;
                var smallRect = new Rectangle(p.X - (size >> 1), p.Y - (size >> 1), size, size);

                g.FillEllipse(brushBig, bigRect);
                g.FillEllipse(brushSmall, smallRect);
                string text = (i + 1).ToString();
                SizeF sf = g.MeasureString(text, ButtonFont);
                float textX = bigRect.Left + (bigRect.Width - sf.Width) / 2f;
                float textY = bigRect.Top + (bigRect.Height - sf.Height) / 2f;
                g.DrawString(text, ButtonFont, brushBig, textX, textY);
            }
            brushSmall.Dispose();
            brushBig.Dispose();
        }
        private Rectangle DrawButton(Graphics g, int w, ref float y)
        {
            y += Padding.Top;

            Rectangle rect = new Rectangle(Padding.Left, (int)y, w - Padding.Left - Padding.Right, ButtonHeight);
            GraphicsPath path = CreatePath(rect, Radius);
            Color fillColor;
            Color foreColor;
            if (_curIndex > 0)
            {
                if (_isHover)
                {
                    fillColor = ButtonHoverColor;
                    foreColor = ButtonForeColor;
                }
                else
                {
                    fillColor = ButtonFillColor;
                    foreColor = ButtonForeColor;
                }
            }
            else
            {
                fillColor = DisableButtonFillColor;
                foreColor = DisableButtonForeColor;
            }
            Brush brushFill = new SolidBrush(fillColor);
            Brush brushFore = new SolidBrush(foreColor);
            g.FillPath(brushFill, path);
            SizeF sf = g.MeasureString(ButtonText, ButtonFont);
            g.DrawString(ButtonText, ButtonFont, brushFore, rect.Left + (rect.Width - sf.Width) / 2f, rect.Top + (rect.Height - sf.Height) / 2f);
            brushFore.Dispose();
            brushFill.Dispose();
            path.Dispose();
            return rect;
        }
        private Rectangle DrawCodeImage(Graphics g, int w, ref float y)
        {
            y += Padding.Top;
            int newW = w - Padding.Left - Padding.Right;
            int h = (int)(newW / 1.5f + 0.5f);
            Rectangle rectangle = new Rectangle(Padding.Left, (int)y, newW, h);
            var result = _result;
            if (result != null && !string.IsNullOrEmpty(result.Key))
            {
                var path = CreatePath(rectangle, Radius);
                g.SetClip(path);
                g.DrawImage(result.Bitmap, rectangle);
                g.ResetClip();
                path.Dispose();
            }
            else
            {
                Pen pen = new Pen(_rectColor);
                g.DrawRectangle(pen, rectangle);
                pen.Dispose();
            }

            y += h;
          
            return rectangle;
        }
        private float DrawText(Graphics g, int w)
        {
            int y = TopHeight + Padding.Top;
            var result = _result;
            if (result == null || string.IsNullOrEmpty(result.Key))
            {
                Brush brush = new SolidBrush(TipColor);
                SizeF tipSizeF = g.MeasureString(TipText, TipFont);
                g.DrawString(TipText, TipFont, brush, Padding.Left, y);
                brush.Dispose();

                return y + tipSizeF.Height;
            }
            else
            {
                Brush brush2 = new SolidBrush(ForeColor);
                string text = result.Word;
                SizeF sf = g.MeasureString(text, Font);
                g.DrawString(text, Font, brush2, w - Padding.Right - sf.Width, y);
                brush2.Dispose();

                Brush brush = new SolidBrush(TipColor);
                SizeF tipSizeF = g.MeasureString(TipText, TipFont);
                g.DrawString(TipText, TipFont, brush, Padding.Left, y + (sf.Height - tipSizeF.Height) / 2f);
                brush.Dispose();
                return y + sf.Height;
            }
        }
        private void FillTopRect(Graphics g, int w)
        {
            if (TopHeight == 0) return;

            Rectangle rect = new Rectangle(0, 0, w, TopHeight);
            var path = CreateTopRoundedPath(rect, Radius);
            Brush brush = new SolidBrush(_isClick? _clickStatus ? SuccessColor : FailureColor : TopColor);
            g.FillPath(brush, path);
            brush.Dispose();
            path.Dispose();
        }
        private void Fill(Graphics g, GraphicsPath path, Rectangle rect)
        {
            Brush brush = new LinearGradientBrush(rect, FillColorStart, FillColorEnd, FillMode);
            g.FillPath(brush, path);
            brush.Dispose();
        }
        private void DrawRect(Graphics g, GraphicsPath path)
        {
            using (Pen pen = new Pen(RectColor, _rectSize))
            {
                g.DrawPath(pen, path);
            }
        }
        private GraphicsPath CreatePath(Rectangle rect, int radius)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90); // 左上角
            path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90); // 右上角
            path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90); // 右下角
            path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90); // 左下角
            path.CloseFigure();
            return path;
        }
        private GraphicsPath CreateTopRoundedPath(Rectangle rect, int radius)
        {
            GraphicsPath path = new GraphicsPath();

            // 左上角圆角
            path.AddArc(rect.X, rect.Y, radius * 2, radius * 2, 180, 90);

            // 上边线
            path.AddLine(rect.X + radius, rect.Y, rect.Right - radius, rect.Y);

            // 右上角圆角
            path.AddArc(rect.Right - radius * 2, rect.Y, radius * 2, radius * 2, 270, 90);

            // 右边线
            path.AddLine(rect.Right, rect.Y + radius, rect.Right, rect.Bottom);

            // 下边线（直角）
            path.AddLine(rect.Right, rect.Bottom, rect.X, rect.Bottom);

            // 左边线（直角）
            path.AddLine(rect.X, rect.Bottom, rect.X, rect.Y + radius);

            path.CloseFigure();
            return path;
        }
        private GraphicsPath CreateBottomRoundedPath(Rectangle rect, int radius)
        {
            GraphicsPath path = new GraphicsPath();

            // 左上角（直角）
            path.AddLine(rect.X, rect.Y, rect.X + radius, rect.Y);

            // 上边线
            path.AddLine(rect.X + radius, rect.Y, rect.Right - radius, rect.Y);

            // 右上角（直角）
            path.AddLine(rect.Right - radius, rect.Y, rect.Right, rect.Y);

            // 右边线
            path.AddLine(rect.Right, rect.Y, rect.Right, rect.Bottom - radius);

            // 右下角圆角
            path.AddArc(rect.Right - radius * 2, rect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);

            // 下边线
            path.AddLine(rect.Right - radius, rect.Bottom, rect.X + radius, rect.Bottom);

            // 左下角圆角
            path.AddArc(rect.X, rect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);

            // 左边线
            path.AddLine(rect.X, rect.Bottom - radius, rect.X, rect.Y);

            path.CloseFigure();
            return path;
        }

    }
}
